//rastrigin problem-depended class
//$Id: rastrigin.h 673 2011-02-27 12:42:40Z Oleg.Bulychov $
#pragma once

#include "src/heo/include/lf_endl.h"
#include "src/heo/include/random.h"
#include "src/heo/include/calc_hash.h"
#include "src/heo/include/reflection.h"
#include "src/heo/include/solution.h"
#include "src/heo/include/transmit_array.h"
#include "src/heo/include/optimization_direction.h"
//#include <math.h>

struct Rastrigin_Config
{
    size_t      limit_output_;
    int         precision_;

    static char const* get_Name() { return "rastrigin"; }
    static char const* get_Section() { return get_Name(); }

    template<class V>
    void accept(V& v)
    {
         v(VAR_(limit_output),               10);
         v(VAR_(precision),                   6);
    }
};
//__________________________________________________________________________________
struct Rastrigin_Problem
{
    size_t n_;
    int a_;
    double lbound_;
    double rbound_;

    explicit Rastrigin_Problem(): n_(0), a_(0), lbound_(0), rbound_(0)
    {}

    static char const* get_Default_File_Name() { return "rastrigin_50_10.txt"; }

    template<class I, class E>
    bool read(I& in, E* env, random_generator& rnd)
    {
        in >> n_ >> a_ >> lbound_ >> rbound_;
        
        if (n_ <= 0 || a_ <= 0 || lbound_ >= rbound_)
            return false;
        
        return true;
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________
class Rastrigin_Solution
{
    //DISALLOW_COPY_AND_ASSIGN(Rastrigin_Solution);
public:
    typedef double                  TCostType;
    typedef minimization<TCostType> TDirection;
protected:
    TCostType       cost_;
    bool            changed_;
    Array<double>   data_;
    unsigned        hash_;
    bool            hash_changed_;
public:
    explicit Rastrigin_Solution():
        cost_(),
        changed_(),
        hash_(),
        hash_changed_()
    {}

    template<class V>
    void accept(V& v)
    {
        v(cost_);
        v(changed_);
        v(data_);
        v(hash_);
        v(hash_changed_);
    }

    void assign(Rastrigin_Solution const& sol)
    {
        changed_        = sol.changed_;
        cost_           = sol.cost_;
        data_           = sol.data_;
        hash_           = sol.hash_;
        hash_changed_   = sol.hash_changed_;
    }

    template<class X>
    void copy_To(X& x) const
    {
        x.assign(*this);
    }

    template<class E>
    void init(E* env, random_generator& rnd)
    {
        data_.resize(env->problem_.n_);
    }

    template<class E>
    void generate(E* env, random_generator& rnd)
    {
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            data_[i] = env->problem_.lbound_+(env->problem_.rbound_-env->problem_.lbound_)*rnd.next_Double();
        }
        changed_ = true;
    }

    TCostType get_Cost() const
    {
        ASSERT_(!changed_);
        return cost_;
    }

    unsigned get_Hash()
    {
        ASSERT_(!changed_);
        if (hash_changed_)
        {
            hash_changed_ = false;
            hash_ = Calc_Hash(&data_[0], data_.size() * sizeof(data_[0]));
        }
        return hash_;
    }

    template<class E>
    void evaluate(E* env, random_generator& rnd)
    {
        if (!changed_)
            return;
        changed_ = false;
        hash_changed_ = true;

        //cost_ =  f_Rastrigin(env);
        //cost_ =  f_Rosenbrock(env);
        //cost_ = f_Griewangk(env);
        cost_ = f_Easom(env);
        //cost_ = f_Schwefel(env);
    }

    template<class E>
    double f_Rastrigin(E* env)
    {
        double f = double(env->problem_.n_)*env->problem_.a_;
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
             f += data_[i]*data_[i]-env->problem_.a_*cos(2.*Math_PI*data_[i]); // x[i]^2-a*cos(2*pi*x[i])
        }
        return f;
    }

    template<class E>
    double f_Rosenbrock(E* env)
    {
        double f = 0;
        for (size_t i = 0; i < env->problem_.n_ - 1; ++i)
        {
            f += 100*(data_[i + 1] - data_[i]*data_[i])*
                     (data_[i + 1] - data_[i]*data_[i]) + 
                     (data_[i]- 1) * (data_[i]- 1);
        }
        return f;
    }

    template<class E>
    double f_Griewangk(E* env)
    {
        double s = 0;
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            s += data_[i]*data_[i]/env->problem_.a_;
        }
        double p = 1;
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            p *= cos(data_[i]/sqrt(double(i+1)));
        }
        return s - p + 1;
    }

    template<class E>
    double f_Easom(E* env)
    {
        double d0 = (data_[0] - Math_PI);
        double d1 = (data_[1] - Math_PI);
        return - cos(data_[0]) * cos(data_[1]) * exp(-( d0 * d0 + d1 * d1));
    }

    template<class E>
    double f_Schwefel(E* env)
    {
        double f = 0;
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            f += - data_[i] * sin(sqrt(abs(data_[i])));
        }
        return f;
    }

    template<class E>
    double r_Rastrigin(E* env)
    {
        // (0,0,...,0) is a global minimum
        double r = 0;
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            r += (data_[i]) * (data_[i]);
        }
        return sqrt(r);
    }

    template<class E>
    double r_Rosenbrock(E* env)
    {
        // (1,1,...,1) is a global minimum
        double r = 0;
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            r += (1 - data_[i]) * (1 - data_[i]);
        }
        return sqrt(r);
    }

    template<class E>
    double r_Griewangk(E* env)
    {
        // (0,0,...,0) is a global minimum
        double r = 0;
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            r += (data_[i]) * (data_[i]);
        }
        return sqrt(r);
    }

    template<class E>
    double r_Easom(E* env)
    {
        // (pi,pi) is a global minimum
        double r0 = data_[0] - Math_PI;
        double r1 = data_[1] - Math_PI;
        return sqrt(r0 * r0 + r1 * r1);
    }

    template<class E>
    double r_Schwefel(E* env)
    {
        // (420.9687,420.9687,...,420.9687) is a global minimum
        double r = 0;
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            r += (420.9687 - data_[i]) * (420.9687 - data_[i]);
        }
        return sqrt(r);
    }

    template<class E>
    void print(std::ostream& os, E* env)
    {
        for (size_t i = 0; i < env->problem_.n_; ++i)
        {
            os.precision(env->cfg_.precision_);
            os << std::fixed << data_[i] << " ";
            if (i > env->cfg_.limit_output_)
            {
                os << "...";
                break;
            }
        }
        os << lf
            << "cost: " << std::fixed << get_Cost() << lf
            //<< "residual: " << r_Rastrigin(env) << lf;
            //<< "residual: " << r_Rosenbrock(env) << lf;
            //<< "residual: " << r_Griewangk(env) << lf;
            << "residual: " << r_Easom(env) << lf;
            //<< "residual: " << r_Schwefel(env) << lf;
    }

    template<class E>
    static bool read(E* env, std::string const& file_name, Solution_Pool<Rastrigin_Solution>& pool)
    {
        return false;
    }

    template<class E>
    void write(E* env, std::string const& file_name, size_t id, size_t count)
    {
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________

//EOF!
